#include "scenecontrol.hpp"
namespace VN{
    bool second=false;
    void ChooseProgress::OnChanged(double v){}
    ChooseProgress::ChooseProgress(Window* w,SDL_Rect& r,double init,SDL_Color back,
        SDL_Color front,int bound):ProgressBar(w,r,init,back,front,bound){}
    const string _audio_path("audio/"),_sound_path("sound/");
    GameScene& GameScene::Choose(vector<TextMod> text,
    vector<Sentence> o,int t,void(*timeout)(GameScene*)){
        limit_value=1./t;
        limit_time->SetProgress(1);
        limit_time->SetVisible(true);
        limit_time->SetEnable(true);
        limit_task=new Task{0,(TaskCallback)timeout,0,(unsigned int)t};
        return Choose(text,o);
    }
    void GameScene::OpenBackLog(){
        backlog->SetVisible(!backlog->visible());
    }
    void GameScene::ChooseLimit(GameScene* g){
        g->limit_time->SetProgress(g->limit_time->GetProgress()-g->limit_value);
        if(g->limit_time->GetProgress()-g->limit_value<0.){
            g->limit_task->finish(g);
            g->EndChoose();
        }
    }
    void GameScene::LoadAudio(){
        auto& m=audio;
        for(auto& s:pack.GetDir("audio/")->file){
            auto v=Mix_LoadMUS_RW(SDL_RWFromConstMem(s.second->c_str(),s.second->size()),1);
            auto temp=s.first;
            for(auto i=temp.begin(),e=temp.end();i!=e;i++){
                if(*i=='.'){
                    temp.erase(i,temp.end());
                    break;
                }
            }
            m.emplace(temp,v);
        }
    }
    void GameScene::LoadSound(){
        auto& m=sound;
        for(auto& s:pack.GetDir("sound/")->file){
            auto v=Mix_LoadWAV_RW(SDL_RWFromConstMem(s.second->c_str(),s.second->size()),1);
            auto temp=s.first;
            for(auto i=temp.begin(),e=temp.end();i!=e;i++){
                if(*i=='.'){
                    temp.erase(i,temp.end());
                    break;
                }
            }
            m.emplace(temp,v);
        }
    }
    GameScene& GameScene::NextAction(int t,Action f){
        next_action=f,next_time=t;
        return *this;
    }
    void GameScene::ShakeCallback(GameScene* g,int t){
        static int _x[8]={-20,0,0,20,20,0,0,-20},
        _y[8]={-20,20,20,0,0,-20,-20,20};
        t--;
        g->game_back->OffsetPosition(_x[t&7],_y[t&7]);
    }
    GameScene& GameScene::ShakeBackground(int times){
        return AddAction(8*times,ShakeCallback);
    }
    bool GameScene::IsCGUnlock(initializer_list<const char*> l){
        for(auto& name:l){
            if(cg.find(name)==cg.end())return false;
        }
        return true;
    }
    GameScene::GameScene(const char* default_language,Font* main_font,initializer_list<SDL_Color> _control_color,initializer_list<int> control_size,initializer_list<UIConfig> config,const char* version,Window* w,SDL_Color default_text_color,const char* main_back_path,const char* init_main_back,
        const char* general_back_path,const char* dialog_back,const char* name_back,
        const char* back_group_name,const char* first_game_back,const char* main_music,SDL_Color time_color,
        initializer_list<ResGroup>& res_pair,initializer_list<Chapter*>& chapters,
        SaveFunc sf,SaveFunc lf,vector<SDL_Point> control_pos,const char* progress_bg,const char* effect_fg,const char* music_fg):version(version),Control(w),save_f(sf),load_f(lf),
        default_text_color(default_text_color),pack("assets"),main_music(main_music),chapter(chapters),res(res_pair.size()),cur_language(default_language),main_font(main_font),control_color(_control_color),ui_size(control_size){
        ui_config.reserve(config.size());
        limit_task=0;
        is_gaming=false;
        music_cache="";
        next_time=0;
        LoadAudio();
        LoadSound();
        auto _r=w->renderer;
        progress_t=_r->LoadTexture(progress_bg);
        music_t=_r->LoadTexture(music_fg);
        effect_t=_r->LoadTexture(effect_fg);
        for(auto& n:res_pair){
            res.emplace(n.name,new ResUnit(_r,pack,n.dir));
        }
        naction=0;
        fade_end=false;
        _auto=0;
        layer=-1;
        _area.x=0,_area.y=0,_area.w=1280,_area.h=720;
        effect_mute=false;
        music_mute=false;
        music_volume=SDL_MIX_MAXVOLUME;
        effect_volume=SDL_MIX_MAXVOLUME;//480*270
        general_back=(Image*)w->AddControl(new Image(w,{0,0},general_back_path));
        cg_go_back=(TextButton*)w->AddControl(new GoBack(control_pos[5],this,"",default_text_color));
        SDL_Rect r{280,0,1000,720};
        cg_view=(ListView*)w->AddControl(new CGList(w,r,40,20,480,270,{255,175,115,130},{255,175,115,130},{255,175,115,130}));
        save_go_back=(TextButton*)w->AddControl(new GoBack(control_pos[5],this,"",default_text_color));
        confirm=(TextButton*)w->AddControl(new ConfirmSaveButton({50,500},this,"",default_text_color));
        save_list=(ListView*)w->AddControl(new TableView(w,r,0,0,500,360,{255,175,115,130},{255,175,115,130},{255,175,115,130}));
        r={50,400,160,60};
        save_name=(InputBox*)w->AddControl(new SaveName(w,r,""));
        option_go_back=(TextButton*)w->AddControl(new GoBack(control_pos[5],this,"",default_text_color));
        auto p6=control_pos[6],p7=control_pos[7];
        p6.x-=default_font->height(),p7.x-=default_font->height();
        effect_box=(CheckBox*)w->AddControl(new EffectBox(p6,this));
        music_box=(CheckBox*)w->AddControl(new MusicBox(p7,this));
        auto s=default_font->height();
        auto& lp=control_pos[12];
        r={lp.x,lp.y,120,s+10};
        language_select=(LanguageSelect*)w->AddControl(new LanguageSelect(this,r,{},s));
        SDL_Rect bar_rect;
        effect_text=(Label*)w->AddControl(new Label(w,control_pos[6],"",default_text_color));
        music_text=(Label*)w->AddControl(new Label(w,control_pos[7],"",default_text_color));
        bgm_text=(Label*)w->AddControl(new Label(w,control_pos[11],"",default_text_color));
        p6=control_pos[6],p7=control_pos[7];
        p6.x+=effect_text->area().w;
        p7.x+=music_text->area().w;
        effect_bar=(TextureBar*)w->AddControl(new EffectBar(SDL_Rect{p6.x,p6.y,512,s},this,effect_t,progress_t));
        music_bar=(TextureBar*)w->AddControl(new MusicBar(SDL_Rect{p7.x,p7.y,512,s},this,music_t,progress_t));
        speed_text=(Label*)w->AddControl(new Label(w,control_pos[8],"",default_text_color));
        speed_normal=(TextButton*)w->AddControl(new AutoPlayNormalButton(control_pos[9],this,"",default_text_color));
        speed_fast=(TextButton*)w->AddControl(new AutoPlayFastButton(control_pos[10],this,"",default_text_color));
        r={0,0,1280,720};
        main_back=(CGImage*)w->AddControl(new CGImage(w));
        start_game=(TextButton*)w->AddControl(new StartGameButton(control_pos[0],this,"",default_text_color));
        load=(TextButton*)w->AddControl(new LoadSaveButton(control_pos[1],this,"",default_text_color));
        enter_cg=(TextButton*)w->AddControl(new EnterCGButton(control_pos[2],this,"",default_text_color));
        main_option=(TextButton*)w->AddControl(new EnterOptionButton(control_pos[3],this,"",default_text_color));
        quit=(TextButton*)w->AddControl(new QuitButton(control_pos[4],w,"",default_text_color));
        SDL_SetTextureBlendMode(start_game->_t,SDL_BLENDMODE_BLEND);
        SDL_SetTextureBlendMode(load->_t,SDL_BLENDMODE_BLEND);
        SDL_SetTextureBlendMode(enter_cg->_t,SDL_BLENDMODE_BLEND);
        SDL_SetTextureBlendMode(main_option->_t,SDL_BLENDMODE_BLEND);
        SDL_SetTextureBlendMode(quit->_t,SDL_BLENDMODE_BLEND);
        back_main=new EnterMainButton({50,650},this,"",default_text_color);
        auto_play=new AutoPlayButton({750,550},this,"",default_text_color);
        go_save=new EnterSaveButton({1000,550},this,"",default_text_color);
        go_load=new LoadSaveButton({900,550},this,"",default_text_color);
        go_option=new EnterOptionButton({1100,550},this,"",default_text_color);
        open_backlog=new OpenBackLogButton({550,550},this,"",default_text_color);
        
        skip=new SkipButton({650,550},this,"",default_text_color);
        r={25,490,200,40};
        name=new NameImage(this,r,name_back);
        r={0,0,1280,720};
        auto unit=res.find(back_group_name)->second;
        game_back=new LayerImage(w,unit,r);
        r.y=520,r.h=200;
        dialog=new AutoPlayText(w,r,dialog_back,(TaskCallback)DialogCallback,this);
        r.y=0;
        r.h=520;
        backlog=new ListView(w,r,2,s,{255,175,115,130},{255,175,115,130},{255,175,115,130});
        r=bgm_text->area();
        r.y+=ui_size[11];
        r.w=200;
        r.h=520-r.y;
        music_list=(ListView*)w->AddControl(new ListView(w,r,10,s,{255,175,115,130},{255,175,115,130},{255,175,115,130}));
        for(auto& n:audio){
            music_list->AddItem(new MusicItem(this,n.first.c_str(),main_font));
        }
        backlog->SetVisible(false);
        dialog->SetEnable(false);
        _visible=true;
        game_back->SetLayerVisible({first_game_back},true).AddLayer({first_game_back});
        cur_ch=chapter.begin();
        on_choose=false;
        r={440,0,400,50};
        limit_time=new ChooseProgress(w,r,1.,Color::Black,time_color);
        limit_time->SetVisible(false);
        limit_time->SetEnable(false);
        is_flash=false;
        for(auto& n:config)ui_config.emplace(n.language,n),language_select->Add(n.language);
        auto g=ui_config.find(default_language);
        if(g!=ui_config.end()){
            auto& c=g->second;
            auto& size=ui_size;
            auto& a=c.text;
            start_game->SetText(a[0],main_font,control_color[0],size[0],{255,255,255,255});
            load->SetText(a[1],main_font,control_color[1],size[1],{255,255,255,255});
            enter_cg->SetText(a[2],main_font,control_color[2],size[2],{255,255,255,255});
            main_option->SetText(a[3],main_font,control_color[3],size[3],{255,255,255,255});
            quit->SetText(a[4],main_font,control_color[4],size[4],{255,255,255,255});
            go_option->SetText(a[3]);
            cg_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            save_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            option_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            effect_text->SetText(a[6],main_font,control_color[6],size[6],{255,255,255,255});
            music_text->SetText(a[7],main_font,control_color[7],size[7],{255,255,255,255});
            auto& er=effect_text->area();
            auto& mr=music_text->area();
            effect_bar->SetPosition(er.x+er.w,er.y);
            music_bar->SetPosition(mr.x+mr.w,mr.y);
            effect_bar->area().h=er.h;
            music_bar->area().h=mr.h;
            speed_text->SetText(a[8],main_font,control_color[8],size[8],{255,255,255,255});
            speed_normal->SetText(a[9],main_font,control_color[9],size[9],{255,255,255,255});
            speed_fast->SetText(a[10],main_font,control_color[10],size[10],{255,255,255,255});
            bgm_text->SetText(a[11],main_font,control_color[11],size[11],{255,255,255,255});
            save_name->SetAlert(a[12]);
            confirm->SetText(a[13]);
            skip->SetText(a[14]);
            auto_play->SetText(a[15]);
            go_load->SetText(a[16]);
            go_save->SetText(a[17]);
            back_main->SetText(a[18]);
            open_backlog->SetText(a[19]);
            auto ch=*cur_ch;
            ch->pos=(*cur_ch)->content.find(default_language)->second.begin();
        }
        else{
            throw "Err:default language error\n";
        }
        unsigned int n=1;
        TableItem* ti;
        auto ch_dir=pack.GetDir("cg/");
        auto& _map=unit->res_map;
        auto& _v=unit->res;
        for(auto& item:ch_dir->file){
            string p(item.first);
            auto ptr=item.second;
            auto i=p.begin(),e=p.end();
            while(i!=e){
                if(*i=='.'){
                    p.erase(i,e);
                    break;
                }
                i++;
            }
            auto t=_belong->renderer->LoadTextureRW(ptr->data(),ptr->size());
            SDL_SetTextureBlendMode(t,SDL_BLENDMODE_BLEND);
            SDL_SetTextureAlphaMod(t,0);
            cg.emplace(p,CGPack{t,false});
        }
        for(auto& v:cg){
            auto o=new CGItem(this,&v.second);
            if(n){
                ti=(TableItem*)cg_view->AddItem(new TableItem());
                n=0;
            }
            else{
                n=1;
            }
            ti->children.push_back(o);
            _map.emplace(v.first,_v.size());
            _v.push_back({v.second.t,false});
        }
        auto f=GetDirFile("save");
        n=1;
        for(auto& item:*f){
            auto o=new SaveObject(("./save/"+*item).c_str());
            save.emplace(o->name,o);
            auto k=new SaveItem(this,o);
            if(n){
                ti=(TableItem*)save_list->AddItem(new TableItem());
                n=0;
            }
            else{
                n=1;
            }
            ti->children.push_back(k);
            delete item;
        }
        delete f;
        ch_dir=pack.GetDir("mainback/");
        for(auto& item:ch_dir->file){
            auto s=item.first;
            auto i=s.begin(),e=s.end();
            while(i!=e){
                if(*i=='.'){
                    break;
                }
                i++;
            }
            auto v=item.second;
            SDL_SetTextureBlendMode(
            backs.emplace(s.substr(0,i-s.begin()),w->renderer->LoadTextureRW(v->data(),v->size()))
            .first->second,SDL_BLENDMODE_BLEND);
        }
        main_back->Show(backs.find(init_main_back)->second);
        EnterMain();
        PlayMusic(main_music);
        cg_whole=(CGImage*)w->AddControl(new CGImage(w));
        FILE* fp=fopen("__","rb");
        unsigned int l;
        fread(&l,4,1,fp);
        while(l--){
            unsigned int v;
            fread(&v,4,1,fp);
            auto cache=new char[v+1]();
            fread(cache,v,1,fp);
            cg.find(cache)->second.unlock=true;
            delete[] cache;
        }
        fclose(fp);
    }
    const char* GameScene::CurrentLanguage(){return cur_language;}
    void GameScene::SetMainBackground(const char* name){
        auto f=backs.find(name);
        if(f!=backs.end()){
            main_back->Show(f->second);
        }
    }
    void GameScene::_press(int x,int y,int clicks,unsigned char key){
        if(!backlog->visible())return;
        auto& r=backlog->area();
        if(r.x<=x&&x<=r.x+r.w&&r.y<=y&&y<=r.y+r.h){
            backlog->pressed=true;
            backlog->_press(x,y,clicks,key);
        }
    }
    void GameScene::_mouse_wheel(SDL_MouseWheelEvent& event){
            if(backlog->visible())backlog->_mouse_wheel(event);
    }
    void GameScene::_mouse_move(int x,int y){
        if(!backlog->visible())return;
        auto& r=backlog->area();
        if(r.x<=x&&x<=r.x+r.w&&r.y<=y&&y<=r.y+r.h){
            backlog->_mouse_move(x,y);
        }
    }
    void GameScene::SetLanguage(const char* name){
        auto f=ui_config.find(name);
        if(f!=ui_config.end()){
            auto& size=ui_size;
            auto& a=f->second.text;
            start_game->SetText(a[0],main_font,control_color[0],size[0],{255,255,255,255});
            load->SetText(a[1],main_font,control_color[1],size[1],{255,255,255,255});
            enter_cg->SetText(a[2],main_font,control_color[2],size[2],{255,255,255,255});
            main_option->SetText(a[3],main_font,control_color[3],size[3],{255,255,255,255});
            quit->SetText(a[4],main_font,control_color[4],size[4],{255,255,255,255});
            go_option->SetText(a[3]);
            cg_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            save_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            option_go_back->SetText(a[5],main_font,control_color[5],size[5],{255,255,255,255});
            effect_text->SetText(a[6],main_font,control_color[6],size[6],{255,255,255,255});
            music_text->SetText(a[7],main_font,control_color[7],size[7],{255,255,255,255});
            auto& er=effect_text->area();
            auto& mr=music_text->area();
            effect_bar->SetPosition(er.x+er.w,er.y);
            music_bar->SetPosition(mr.x+mr.w,mr.y);
            effect_bar->area().h=er.h;
            music_bar->area().h=mr.h;
            speed_text->SetText(a[8],main_font,control_color[8],size[8],{255,255,255,255});
            speed_normal->SetText(a[9],main_font,control_color[9],size[9],{255,255,255,255});
            speed_fast->SetText(a[10],main_font,control_color[10],size[10],{255,255,255,255});
            bgm_text->SetText(a[11],main_font,control_color[11],size[11],{255,255,255,255});
            save_name->SetAlert(a[12]);
            confirm->SetText(a[13]);
            skip->SetText(a[14]);
            auto_play->SetText(a[15]);
            go_load->SetText(a[16]);
            go_save->SetText(a[17]);
            back_main->SetText(a[18]);
            open_backlog->SetText(a[19]);
            auto ch=*cur_ch;
            auto& c=ch->content.find(name)->second;
            ch->pos=c.begin()+(ch->pos-ch->content.find(cur_language)->second.begin());
            cur_language=name;
            ProcessText();
        }
    }
    //前提：保证t有效
    void GameScene::ShowCG(SDL_Texture* t){
        cg_whole->SetEnable(true);
        cg_whole->SetVisible(true);
        cg_whole->Show(t);
    }

    void GameScene::FlashCheck(GameScene* g){
        if(g->flash_in){
            if(g->flash_t++==g->flash_duration){
                g->flash_in=false;
                g->flash_t=0;
                return;
            }
            g->flash_color.a+=g->flash_value;
            return;
        }
        //淡出
        if(g->flash_t++==g->flash_duration){
            if(--g->flash_time){
                g->flash_in=true;
                g->flash_t=0;
                return;
            }
            g->is_flash=false;
            g->_belong->RemoveTask(g->flash_task);
            if(!--g->naction)g->fade_end=true;
            return;
        }
        g->flash_color.a-=g->flash_value;
    }

    void GameScene::FlashCheckTexture(GameScene* g){
        if(g->flash_in){
            if(g->flash_t++==g->flash_duration){
                g->flash_in=false;
                g->flash_t=0;
                return;
            }
            g->flash_color.a+=g->flash_value;
            g->flash_target->SetAlpha(g->flash_color.a);
            return;
        }
        //淡出
        if(g->flash_t++==g->flash_duration){
            if(--g->flash_time){
                g->flash_in=true;
                g->flash_t=0;
                return;
            }
            g->is_flash=false;
            g->_belong->RemoveTask(g->flash_task);
            if(!--g->naction)g->fade_end=true;
            return;
        }
        g->flash_color.a-=g->flash_value;
        g->flash_target->SetAlpha(g->flash_color.a);
    }
    GameScene& GameScene::Flash(LayerImage* target,SDL_Rect area,unsigned char init_alpha,size_t times,size_t t){
        if(is_flash)return *this;
        flash_value=init_alpha/t;
        target->SetAlpha(0);
        target->_area=area;
        flash_target=target;
        naction++;
        flash_t=0;
        flash_duration=t;
        flash_color.a=0;
        flash_time=times;
        fade_end=false;
        flash_in=true;
        is_flash=true;
        flash_area=area;
        flash_mode=false;
        flash_task=_belong->AddTask((void(*)(void*))FlashCheckTexture,this,1,true,true);
        return *this;
    }

    GameScene& GameScene::Flash(SDL_Color color,size_t times,size_t t){
        if(is_flash)return *this;
        flash_value=color.a/t;
        naction++;
        flash_t=0;
        flash_duration=t;
        flash_color=color;
        flash_color.a=0;
        flash_time=times;
        fade_end=false;
        flash_in=true;
        is_flash=true;
        flash_area={0,0,1280,720};
        flash_mode=true;
        flash_task=_belong->AddTask((void(*)(void*))FlashCheck,this,1,true,true);
        return *this;
    }
    GameScene::~GameScene(){
        delete limit_time;
        for(auto& n:chapter)delete n;
        for(auto& n:character)delete n.second;
        for(auto& n:res)delete n.second;
        for(auto& n:sound)Mix_FreeChunk(n.second);
        for(auto& n:audio)Mix_FreeMusic(n.second);
        delete game_back;
        delete name;
        delete dialog;
        delete auto_play;
        delete go_save;
        delete go_load;
        delete go_option;
        delete back_main;
        delete skip;
        delete backlog;
        delete open_backlog;
    }
    void GameScene::CGChangeFade(GameScene* g){
        g->game_back->SetAlpha(g->game_back->Alpha()-1);
    }
    void GameScene::CGChangeFade2(GameScene* g){
        g->_belong->AddTask((TaskCallback)CGChangeFade3,g,255,true,false,
        (TaskCallback)SceneFadeFinish,g);
        g->game_back->Reset().SetLayerVisible(g->next_scene_name,true).
        ClearLayer().AddLayer(g->next_scene_name);
    }
    void GameScene::CGChangeFade3(GameScene* g){
        g->game_back->SetAlpha(g->game_back->Alpha()+1);
    }
    void GameScene::SceneChangeFade(GameScene* g){
        g->game_back->SetAlpha(g->game_back->Alpha()-5);
    }
    void GameScene::SceneChangeFade2(GameScene* g){
        g->_belong->AddTask((TaskCallback)SceneChangeFade3,g,51,true,false,
        (TaskCallback)SceneFadeFinish,g);
        g->game_back->Reset().SetLayerVisible(g->next_scene_name,true).
        ClearLayer().AddLayer(g->next_scene_name);
    }
    void GameScene::SceneChangeFade3(GameScene* g){
        g->game_back->SetAlpha(g->game_back->Alpha()+5);
    }
    void GameScene::SceneFadeFinish(GameScene* g){
        if(!--g->naction)g->fade_end=true;
    }
    void GameScene::CharacterFadein(LayerImage* g){
        g->SetAlpha(g->Alpha()+5);
    }
    void GameScene::CharacterFadeout(LayerImage* g){
        g->SetAlpha(g->Alpha()-5);
    }
    GameScene& GameScene::Jump(const char* name){
        auto cur=*cur_ch;
        auto& m=cur->label;
        auto f=m.find(name);
        if(f!=m.end()){
            last_name=this->name->cache;
            SaveBackLog();
            auto& l=cur->content.find(cur_language)->second;
            cur->pos=l.begin()+f->second.find(cur_language)->second;
            ProcessText();
        }
        return *this;
    }
    GameScene& GameScene::Fadein(LayerImage* a){
        fade_end=false;
        naction++;
        _belong->AddTask((TaskCallback)CharacterFadein,a,51,true,false,
        (TaskCallback)SceneFadeFinish,this);
        return *this;
    }
    GameScene& GameScene::StopSound(){
        Mix_HaltChannel(-1);
        return *this;
    }
    GameScene& GameScene::Fadeout(LayerImage* a){
        fade_end=false;
        naction++;
        _belong->AddTask((TaskCallback)CharacterFadeout,a,51,true,false,
        (TaskCallback)SceneFadeFinish,this);
        return *this;
    }
    GameScene& GameScene::ChangeBackground(initializer_list<const char*>visible_name,unsigned int mode){
        last_scene_name=next_scene_name;
        next_scene_name=visible_name;
        switch(mode){
            case sc_default:
            case sc_cg_fade:
            naction++;
            fade_end=false;
            _belong->AddTask((TaskCallback)SceneChangeFade,this,51,true,false,
            (TaskCallback)SceneChangeFade2,this);
            return *this;
            case sc_dissolve:
            case sc_cg_dissolve:
            return CrossDissolve(game_back,visible_name);
        }
        return *this;
    }
    void GameScene::ActionFinish(ActionPack* g){
        if(!--g->g->naction){
            g->g->fade_end=true;
        }
        g->g->actions.erase(g->pos);
        delete g;
    }
    void GameScene::ActionCallback(ActionPack* g){
        g->f(g->g,++g->t);
    }
    GameScene::DissolvePack::DissolvePack(GameScene*g,LayerImage* l,
    initializer_list<const char*>& next):g(g),l(l),next(next),t(0),last(l->last){}
    GameScene::DissolvePack::DissolvePack(GameScene*g,LayerImage* l,
    initializer_list<const char*>& last,initializer_list<const char*>&next):g(g),l(l),last(last),next(next),t(0){}
    GameScene& GameScene::CrossDissolve(LayerImage* l,initializer_list<const char*>& next){
        naction++;
        fade_end=false;
        auto pack=new DissolvePack(this,l,next);
        _belong->AddTask((TaskCallback)DissolveCallback,pack,51,true,false,
        (TaskCallback)DissolveFinish,pack);
        l->AddLayer(next).SetLayerVisible(next,true).SetAlpha(next,0);
        return *this;
    }
    GameScene& GameScene::CrossDissolveSlow(LayerImage* l,initializer_list<const char*>& next){
        naction++;
        fade_end=false;
        auto pack=new DissolvePack(this,l,next);
        _belong->AddTask((TaskCallback)DissolveSlowCallback,pack,255,true,false,
        (TaskCallback)DissolveFinish,pack);
        l->SetLayerVisible(next,true).SetAlpha(next,0).AddLayer(next);
        return *this;
    }
    GameScene& GameScene::CrossDissolve(LayerImage* l,initializer_list<const char*> last,
        initializer_list<const char*> next){
        naction++;
        fade_end=false;
        auto pack=new DissolvePack(this,l,last,next);
        _belong->AddTask((TaskCallback)DissolveCallback,pack,51,true,false,
        (TaskCallback)DissolveFinish,pack);
        l->SetLayerVisible(next,true).SetAlpha(next,0).AddLayer(next);
        return *this;
    }
    GameScene& GameScene::CrossDissolveSlow(LayerImage* l,initializer_list<const char*> last,
        initializer_list<const char*> next){
        naction++;
        fade_end=false;
        auto pack=new DissolvePack(this,l,last,next);
        _belong->AddTask((TaskCallback)DissolveSlowCallback,pack,255,true,false,
        (TaskCallback)DissolveFinish,pack);
        l->SetLayerVisible(next,true).SetAlpha(next,0).AddLayer(next);
        return *this;
    }
    void GameScene::DissolveCallback(DissolvePack* g){
        auto v=5*(++g->t);
        g->l->SetAlpha(g->last,255-v).SetAlpha(g->next,v);
    }
    void GameScene::DissolveSlowCallback(DissolvePack* g){
        auto v=++g->t;
        g->l->SetAlpha(g->last,255-v).SetAlpha(g->next,v);
    }
    void GameScene::DissolveFinish(DissolvePack* g){
        if(!--g->g->naction){
            g->g->fade_end=true;
        }
        g->l->PopLayer(g->last.size()+g->next.size()).AddLayer(g->next);
        delete g;
    }
    GameScene::ActionPack::ActionPack(GameScene*g,int t,Action a):g(g),t(t),f(a){}
    GameScene& GameScene::AddAction(int t,Action a){
        if(!t)return *this;
        naction++;
        fade_end=false;
        auto v=new ActionPack(this,0,a);
        v->pos=actions.insert(actions.end(),v);
        _belong->AddTask((TaskCallback)ActionCallback,v,t,true,false,(TaskCallback)ActionFinish,v);
        return *this;
    }
    GameScene& GameScene::SetBackgroundVisible(const char* name,bool v){
        game_back->SetLayerVisible({name},v);
        return *this;
    }
    GameScene& GameScene::PlaySound(const char* name,int times){
        auto f=sound.find(name);
        if(f!=sound.end()){
            Mix_PlayChannel(-1,f->second,times);
        }
        return *this;
    }
    GameScene& GameScene::PlayMusic(const char* name){
        auto f=audio.find(name);
        if(f!=audio.end()){
            Mix_PlayMusic(f->second,-1);
            music_cache=name;
        }
        return *this;
    }
    LayerImage* GameScene::AddCharacter(const char* group,
    initializer_list<const char*>visible_name,SDL_Rect& r){
        auto f=res.find(group);
        if(f!=res.end()){
            auto u=f->second;
            auto temp=new LayerImage(_belong,u,r);
            auto& m=u->res_map;
            auto& a=u->res;
            for(auto& n:visible_name){
                auto g=m.find(n);
                if(g!=m.end()){
                    a[g->second].visible=true;
                }
            }
            character.emplace(group,temp);
            temp->AddLayer(visible_name);
            return temp;
        }
        return 0;
    }
    
    LayerImage* GameScene::GetCharacter(const char* group){
        auto f=character.find(group);
        if(f!=character.end())return f->second;
        return 0;
    }
    GameScene& GameScene::RemoveCharacter(const char* group){
        auto f=character.find(group);
        if(f!=character.end()){
            delete f->second;
            character.erase(f);
        }
        return *this;
    }
    GameScene& GameScene::StopMusic(){
        Mix_HaltMusic();
        music_cache="";
        return *this;
    }
    GameScene& GameScene::Wait(int t){
        return *this;
    }
    void GameScene::SetAutoSpeed(bool fast){
        if(fast){
            dialog->PlayFast();
            return;
        }
        dialog->Play();
    }
    void GameScene::_release(int x,int y,int clicks,unsigned char key){
        auto& r=backlog->area();
        if(r.x<=x&&x<=r.x+r.w&&r.y<=y&&y<=r.y+r.h&&backlog->visible()){
            backlog->pressed=false;
            return;
        }
        if(dialog->enable()&&_enabled){
            auto& r3=auto_play->area();
            if(r3.x<=x&&x<=r3.x+r3.w&&r3.y<=y&&y<=r3.y+r3.h){
                auto_play->_release(x,y,clicks,key);
                return;
            }
            auto& r4=back_main->area();
            if(r4.x<=x&&x<=r4.x+r4.w&&r4.y<=y&&y<=r4.y+r4.h){
                back_main->_release(x,y,clicks,key);
                return;
            }
            auto& r5=open_backlog->area();
            if(r5.x<=x&&x<=r5.x+r5.w&&r5.y<=y&&y<=r5.y+r5.h){
                open_backlog->_release(x,y,clicks,key);
                return;
            }
        }
        if(!_enabled||!fade_end||!dialog->enable())return;
        auto& r1=go_option->area();
        if(r1.x<=x&&x<=r1.x+r1.w&&r1.y<=y&&y<=r1.y+r1.h){
            go_option->_release(x,y,clicks,key);
            return;
        }
        auto& r2=go_save->area();
        if(r2.x<=x&&x<=r2.x+r2.w&&r2.y<=y&&y<=r2.y+r2.h){
            go_save->_release(x,y,clicks,key);
            return;
        }
        auto& r4=go_load->area();
        if(r4.x<=x&&x<=r4.x+r4.w&&r4.y<=y&&y<=r4.y+r4.h){
            go_load->_release(x,y,clicks,key);
            return;
        }
        for(auto& n:choose){
            auto& r=n->area();
            if(r.x<=x&&x<=r.x+r.w&&r.y<=y&&y<=r.y+r.h){
                n->_release(x,y,clicks,key);
                return;
            }
        }
        if(on_choose)return;
        dialog->_release(x,y,clicks,key);
    }
    void GameScene::_draw(){
        auto r=_belong->renderer;
        if(in_main){
            return;
        }
        if(limit_task){
            if(++limit_task->cur<limit_task->max)ChooseLimit(this);
            else{
                delete limit_task;
                limit_task=0;
            }
        }
        auto mod=SDL_GetModState();
        if(mod==KMOD_LCTRL||mod==KMOD_RCTRL)Skip();
        if(_enabled){
            game_back->_draw();
            if(limit_time->visible())limit_time->_draw();
            for(auto& n:character)n.second->_draw();
            if(is_flash){
                auto r=_belong->renderer;
                if(flash_mode)
                {
                    r->SetColor(flash_color);
                    r->FillRect(flash_area);
                }
                else{
                    flash_target->_draw();
                }
            }
            for(auto& n:choose)n->_draw();
            if(dialog->enable()){
                dialog->_draw();
                if(backlog->visible())backlog->_draw();
                if(name->visible())name->_draw();
                auto_play->_draw();
                back_main->_draw();
                open_backlog->_draw();
                if(fade_end){
                    go_save->_draw();
                    go_load->_draw();
                    go_option->_draw();
                }
            }
        }
    }
    void GameScene::SetEnable(bool v){
        cg_go_back->SetEnable(v);
        cg_view->SetEnable(v);
        save_go_back->SetEnable(v),confirm->SetEnable(v);
        save_list->SetEnable(v);
        option_go_back->SetEnable(v);
        effect_box->SetEnable(v),music_box->SetEnable(v);
        effect_bar->SetEnable(v),music_bar->SetEnable(v);
        effect_text->SetEnable(v),music_text->SetEnable(v),speed_text->SetEnable(v);
        main_back->SetEnable(v);
        start_game->SetEnable(v),load->SetEnable(v),enter_cg->SetEnable(v),quit->SetEnable(v);
        for(auto& n:character)n.second->SetEnable(v);
        auto_play->SetEnable(v);
        name->SetEnable(v),game_back->SetEnable(v);
        dialog->SetEnable(v);
    }
    void GameScene::_draw_edge(){}
    void GameScene::SetVisible(bool v){
        cg_go_back->SetVisible(v);
        cg_view->SetVisible(v);
        save_go_back->SetVisible(v),confirm->SetVisible(v);
        save_list->SetVisible(v);
        option_go_back->SetVisible(v);
        effect_box->SetVisible(v),music_box->SetVisible(v);
        effect_bar->SetVisible(v),music_bar->SetVisible(v);
        effect_text->SetVisible(v),music_text->SetVisible(v),speed_text->SetVisible(v);
        main_back->SetVisible(v);
        start_game->SetVisible(v),load->SetVisible(v),enter_cg->SetVisible(v),quit->SetVisible(v);
        for(auto& n:character)n.second->SetVisible(v);
        auto_play->SetVisible(v);
        name->SetVisible(v),game_back->SetVisible(v);
        dialog->SetVisible(v);
    }
    bool GameScene::IsAutoPlay(){return _auto;}
    GameScene& GameScene::Lock(){
        need_lock=true;
        return *this;
    }
    ResUnit* GameScene::QueryRes(const char* name){
        auto f=res.find(name);
        if(f!=res.end()){
            return f->second;
        }
        return 0;
    }
    GameScene& GameScene::Choose(vector<TextMod> text,vector<Sentence> s){
        last_name=name->cache;
        choose.clear();
        choose.resize(s.size());
        on_choose=true;
        SaveBackLog();
        dialog->SetText(text);
        auto b=dialog->Background();
        auto cur=choose.begin();
        auto w=_belong;
        int y=80;
        for(auto& n:s){
            auto c=new ChooseButton(b,this,n.text,n.logic);
            *cur=c;
            auto& a=c->area();
            auto& b=c->_rect;
            a.y=y;
            b.x=(1280>>1)-(b.w>>1),b.y=y+25-(b.h>>1);
            y+=75;
            cur++;
        }
        return *this;
    }
    void GameScene::EndChoose(){
        for(auto& n:choose)delete n;
        choose.clear();
        on_choose=false;
        limit_time->SetVisible(false);
        limit_time->SetEnable(false);
        if(limit_task){
            delete limit_task;
            limit_task=0;
        }
    }
    void GameScene::MuteEffect(){
        if(effect_mute){
            Mix_Volume(-1,effect_volume);
            effect_mute=false;
            return;
        }
        effect_mute=true;
        Mix_Volume(-1,0);
    }
    void GameScene::MuteMusic(){
        if(music_mute){
            Mix_VolumeMusic(music_volume);
            music_mute=false;
            return;
        }
        music_mute=true;
        Mix_VolumeMusic(0);
    }
    void GameScene::Back(){
        if(in_main){
            EnterMain();
            return;
        }
        EnterGame();
        dialog->SetEnable(dialog_state);
        name->SetVisible(name_state);
    }
    void GameScene::SetMusicVolume(double p){
        Mix_VolumeMusic(SDL_MIX_MAXVOLUME*p);
    }
    void GameScene::SetEffectVolume(double p){
        Mix_Volume(-1,p*SDL_MIX_MAXVOLUME);
    }
    void GameScene::AutoPlayCallback(GameScene* a){
        if(a->dialog->Finish())DialogCallback(a);
    }
    void GameScene::AutoPlay(){
        if(_auto){
            _belong->RemoveTask(_auto);
            _auto=0;
            return;
        }
        _auto=_belong->AddTask((TaskCallback)AutoPlayCallback,this,60,false,true);
    }
    void GameScene::GameDialogFadein(AutoPlayText* g){
        g->SetAlpha(g->Alpha()+5);
    }
    void GameScene::GameDialogFadeout(AutoPlayText* g){
        g->SetAlpha(g->Alpha()-5);
    }
    void GameScene::GameBackFinish(GameScene* g){
        g->dialog->SetVisible(true);
        g->_belong->AddTask((TaskCallback)GameDialogFadein,g->dialog,51,true,
        false,(TaskCallback)GameEntered,g);
    }
    void GameScene::EndGame(){
        is_gaming=false;
        second=true;
        EnterMain();
    }
    void GameScene::GameEntered(GameScene* g){
        g->fade_end=true;
        g->need_lock=false;
        g->on_choose=false;
        g->cur_ch=g->chapter.begin();
        auto ch=*g->cur_ch;
        ch->pos=ch->content.find(g->cur_language)->second.begin();
        g->ProcessText();
        g->dialog->SetEnable(true);
        g->dialog->Play();
    }
    bool GameScene::IsGaming(){return is_gaming;}
    void GameScene::StartGame(){
        EnterGame();
        if(is_gaming)return;
        StopMusic();
        is_gaming=true;
        dialog->SetAlpha(0);
        dialog->SetEnable(false);
        game_back->ClearLayer();
        game_back->SetAlpha(0);
        backlog->Clear();
        _belong->AddTask((TaskCallback)GameBackFadein,game_back,51,
        true,false,(TaskCallback)GameBackFinish,this);
    }
    void GameScene::SetSaveInput(const char* text){
        save_name->SetText(text);
    }
    void GameScene::Save(const string& name){
        auto f=save.find(name);
        auto o=new SaveObject(this,name,save_f);
        if(f==save.end()){
            //新存档
            auto k=new SaveItem(this,o);
            if(save.size()&1){
                //奇数
                ((TableItem*)save_list->GetItem(save_list->count()-1))->children.push_back(k);
            }
            else{
                ((TableItem*)save_list->AddItem(new TableItem()))->children.push_back(k);
            }
            save.emplace(name,o);
            return;
        }
        f->second->item->Update(o);
        delete f->second;
        f->second=o;
    }
    void GameScene::Load(const string& name){
        auto f=save.find(name);
        if(f==save.end())return;
        for(auto& c:character)delete c.second;
        for(auto& c:choose)delete c;
        choose.clear();
        character.clear();
        EnterGame();
        is_gaming=true;
        auto s=f->second;
        s->Load(this,load_f);
        backlog->Clear();
        need_lock=false;
        on_choose=false;
        dialog->SetAlpha(255);
        fade_end=true;
        dialog->SetEnable(true);
        dialog->Play();
    }
    void GameScene::ConfirmSave(){
        //对于存档 列表选择时会将存档名设置到输入框中 如果输入框=已有则为覆盖
        //对于读档 当且仅当输入框=已有才允许读取 读取结束后切到游戏界面
        auto& s=*save_name->text();
        if(s.empty())return;
        if(is_load){
            Load(s);
            return;
        }
        Save(s);
    }
    void GameScene::ProcessText(){
        if(cur_ch==chapter.end())return;
        auto ch=*cur_ch;
        auto& pos=ch->pos;
        if(pos==ch->content.find(cur_language)->second.end())return;
        dialog->SetText(*pos->text);
    }
    void GameScene::DialogCallback(GameScene* g){
        if(!g->_enabled||g->on_choose||!g->fade_end||!g->dialog->enable())return;
        auto& cur_ch=g->cur_ch;
        if(cur_ch==g->chapter.end()){
            //结束游戏
            return;
        }
        auto ch=*cur_ch;
        auto& pos=ch->pos;
        auto& content=ch->content.find(g->cur_language)->second;
        if(pos==content.end()){
            cur_ch++;
            //下一章
            g->ProcessText();
            return;
        }
        g->last_name=g->name->cache;
        pos->logic(g);
        if(g->on_choose||g->need_lock){
            g->need_lock=false;
            return;
        }
        g->SaveBackLog();
        g->AddAction(g->next_time,g->next_action).next_time=0;
        pos++;
        g->ProcessText();
    }
    void GameScene::SaveBackLog(){
        auto b=backlog;
        auto w=(Window*)window();
        auto item=(BackLogItem*)b->AddItem(new BackLogItem(w,
        last_name,dialog->_cache));
        auto t=item->content_t;
        int sum=0;
        for(size_t i=0,max=item->info.size();i<max;i++){
            auto& info=item->info[i];
            b->AddItem(new LogTextItem(w,info.first,info.second,sum,t));
            sum+=info.second;
        }
    }
    GameScene& GameScene::Next(){
        SaveBackLog();
        AddAction(next_time,next_action).next_time=0;
        (*cur_ch)->pos++;
        ProcessText();
        return *this;
    }
    GameScene& GameScene::HideDialog(){
        dialog->SetEnable(false);
        _belong->AddTask((TaskCallback)GameDialogFadeout,dialog,51,true);
        return *this;
    }
    GameScene& GameScene::ShowDialog(){
        dialog->SetEnable(true);
        _belong->AddTask((TaskCallback)GameDialogFadein,dialog,51,true);
        return *this;
    }

    void GameScene::Redirect(unsigned int ch,unsigned int index){
        cur_ch=chapter.begin()+ch;
        (*cur_ch)->pos=(*cur_ch)->content.find(cur_language)->second.begin()+index;
        ProcessText();
    }
    void GameScene::GetChapterInfo(size_t& ch,size_t& index){
        auto c=*cur_ch;
        ch=cur_ch-chapter.begin();
        index=c->pos-c->content.find(cur_language)->second.begin();
    }
    void GameScene::Skip(){
        if(!is_gaming)return;
        _belong->FinalizeTask();
        DialogCallback(this);
    }
    GameScene& GameScene::UnlockCG(const char* name){
        auto f=cg.find(name);
        if(f!=cg.end()){
            f->second.unlock=true;
            vector<string> cache;
            for(auto& n:cg){
                if(n.second.unlock)cache.push_back(n.first);
            }
            FILE* fp=fopen("__","wb");
            size_t l=cache.size();
            fwrite(&l,4,1,fp);
            for(auto& n:cache){
                l=n.size();
                fwrite(&l,4,1,fp);
                fwrite(n.c_str(),l,1,fp);
            }
            fclose(fp);
        }
        return *this;
    }
    SceneState* GameScene::QueryState(FILE* fp,size_t& total){
        auto state=GetState();
        state->Write(fp);
        total+=state->total;
        return state;
    }
    SceneState* GameScene::GetState(){
        auto temp=new SceneState();
        GetChapterInfo(temp->ch,temp->index);
        temp->music=music_cache;
        temp->back.resize(game_back->GetMemberNum());
        game_back->QueryLayer(temp->back);
        temp->character.resize(character.size());
        auto begin=character.begin();
        for(auto& ch:temp->character){
            begin->second->QueryLayer(ch.ch);
            auto& s=begin->first;
            auto _s=new char[s.length()+1]();
            memcpy(_s,s.c_str(),s.length());
            ch.name=_s;
            ch.rect=begin->second->area();
            begin++;
        }
        auto ch=*cur_ch;
        temp->content=*ch->pos->text;
        temp->index=ch->pos-ch->content.find(cur_language)->second.begin();
        temp->name=name->cache;
        return temp;
    }

    SaveObject::SaveObject(GameScene* g,const string n,SaveFunc f){
        state=g->GetState();
        name=new char[n.length()+1]();
        memcpy(name,n.c_str(),n.length());
        string p("./save/"+n);
        FILE* fp=fopen(p.c_str(),"wb");
        auto l=n.length();
        auto game_back=g->game_back;
        auto _nb=game_back->GetMemberNum();
        time_t nowtime=time(NULL); //获取计算机从1970年1月1日到目前过去的总秒数
        time_s=new char[64]();
        strftime(time_s,64,"%Y-%m-%d %H:%M:%S",localtime(&nowtime));
        size_t ch,index;
        g->GetChapterInfo(ch,index);
        //统一写入
        fwrite("LBVN",4,1,fp);
        fwrite(g->version,4,1,fp);
        fwrite(&l,4,1,fp);
        fwrite(n.c_str(),l,1,fp);
        fwrite(time_s,64,1,fp);
        total=80+l;
        state=g->QueryState(fp,total);
        f(fp);
        fclose(fp);
        memcpy(version,g->version,4);
    }
    SaveObject::SaveObject(const char* path){
        FILE* fp=fopen(path,"rb");
        char buffer[4];
        fread(buffer,4,1,fp);
        if(strncmp(buffer,"LBVN",4)){
            throw SaveException();
        }
        fread(version,4,1,fp);
        unsigned int nl;
        fread(&nl,4,1,fp);
        name=new char[nl+1]();
        time_s=new char[64]();
        fread(name,nl,1,fp);
        fread(time_s,64,1,fp);
        total=80+nl;
        state=new SceneState();
        state->Load(fp);
        total+=state->total;
        fclose(fp);
    }
    SceneState::~SceneState(){
        for(auto& n:cache)delete[] n;
    }
    void SaveObject::Load(GameScene* g,SaveFunc f){
        string path("save/");
        path.append(name);
        FILE* fp=fopen(path.c_str(),"rb");
        fseek(fp,total,SEEK_CUR);
        f(fp);
        fclose(fp);
        if(!state->music.empty()){
            g->StopMusic();
            g->PlayMusic(state->music.c_str());
        }
        state->Load(g);
    }
    SaveObject::~SaveObject(){
        delete[] name;
        delete[] time_s;
        delete state;
    }
}